
import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
        // InputStream就是Java标准库提供的最基本的输入流
        // InputStream并不是一个接口，而是一个抽象类，它是所有输入流的超类。这个抽象类定义的一个最重要的方法就是int read()
        // public abstract int read() throws IOException;
        // InputStream和OutputStream都是通过close()方法来关闭流
        // 在调用InputStream的read()方法读取数据时，我们说read()方法是阻塞（Blocking）的
        readFile();
        tryReadFile();
        cacheReadFile();
        byteArrayInputStream();
        readAsString("./readme.md");
    }

    // try ... finally来保证InputStream在无论是否发生IO错误的时候都能够正确地关闭
    public static void readFile() throws IOException {
        InputStream input = null;
        try {
            input = new FileInputStream("./readme.md");
            int n;
            // input.read() 每次读取一个字节，返回值是0~255，如果到达文件末尾，返回-1
            while ((n = input.read()) != -1) { // 利用while同时读取并判断
                System.out.println(n);
            }
        } finally {
            if (input != null) {
                input.close();
            }
        }
    }

    // Java 7引入的新的try(resource)的语法，只需要编写try语句，让编译器自动为我们关闭资源
    // 编译器并不会特别地为InputStream加上自动关闭。编译器只看try(resource =
    // ...)中的对象是否实现了java.lang.AutoCloseable接口，
    // 如果实现了，就自动加上finally语句并调用close()方法。
    // InputStream和OutputStream都实现了这个接口，因此，都可以用在try(resource)
    public static void tryReadFile() throws IOException {
        try (InputStream input = new FileInputStream("./readme.md")) {
            int n;
            while ((n = input.read()) != -1) {
                System.out.println(n);
            }
        } // 编译器在此自动为我们写入finally并调用close()
    }

    // 读取到缓冲区，而不是每次只读取一个字节
    // int read(byte[] b)：读取若干字节并填充到byte[]数组，返回读取的字节数
    // int read(byte[] b, int off, int len)：指定byte[]数组的偏移量和最大填充数
    public static void cacheReadFile() throws IOException {
        try (InputStream input = new FileInputStream("./readme.md")) {
            // 定义1000个字节大小的缓冲区:
            byte[] buffer = new byte[1000];
            int n;
            while ((n = input.read(buffer)) != -1) { // 读取到缓冲区
                System.out.println("read " + n + " bytes.");
            }
        }
    }

    // ByteArrayInputStream可以在内存中模拟一个InputStream
    public static void byteArrayInputStream() throws IOException {
        byte[] data = { 72, 101, 108, 108, 111, 33 };
        try (InputStream input = new ByteArrayInputStream(data)) {
            int n;
            while ((n = input.read()) != -1) {
                System.out.println((char) n);
            }
        }
    }

    // 举个例子：我们想从文件中读取所有字节，并转换成char然后拼成一个字符串，可以这么写
    public static void readAsString(String filename) throws IOException {
        try (InputStream input = new FileInputStream(filename)) {
            StringBuilder sb = new StringBuilder();
            int n;
            while ((n = input.read()) != -1) {
                sb.append((char) n);
            }
            System.out.println("文件内容为： " + sb.toString());
        }
    }
}
